home *** CD-ROM | disk | FTP | other *** search
- #define LIBQBUILD_CORE
- #include "../include/libqbuild.h"
-
- int bitbytes; /* (num_visleafs+63)>>3 */
- int bitlongs;
- int c_chains;
- int c_leafsee, c_portalsee;
- int c_portalskip, c_leafskip;
- int c_portaltest, c_portalpass, c_portalcheck;
- int c_vistest, c_mighttest;
- int count_sep;
- int leafon; /* the next leaf to be given to a thread to process */
- int originalvismapsize;
- int testvislevel = 2;
- int testvisfastlevel = 3;
- int totalvis;
- unsigned char *uncompressed; /* [bitbytes*num_visleafs] */
-
- /*
- * These variables are used by both BasePortalVis and SimpleFlood.
- * portalsee is an array of bytes. When running BasePortalVis,
- * the array is filled with a 'mightsee' indicator
- * c_* is counters.
- */
- unsigned char portalsee[MAX_MAP_PORTALS];
-
- void CheckStack(register struct visleaf *leaf, register threaddata_t * thread)
- {
- pstack_t *p;
-
- for (p = thread->pstack_head.next; p; p = p->next)
- if (p->leaf == leaf)
- Error("CheckStack: leaf recursion");
- }
-
- /*
- * ==============
- * ClipToSeperators
- *
- * Source, pass, and target are an ordering of portals.
- *
- * Generates seperating planes canidates by taking two points from source and one
- * point from pass, and clips target by them.
- *
- * If target is totally clipped away, that portal can not be seen through.
- *
- * Normal clip keeps target on the same side as pass, which is correct if the
- * order goes source, pass, target. If the order goes pass, source, target then
- * flipclip should be set.
- * ==============
- */
- #ifndef NEWVIS
- struct winding *ClipToSeperators(register struct winding *source, register struct winding *pass, register struct winding *target, register bool flipclip)
- {
- int i, j, k, l;
- struct plane plane;
- vec3_t v1, v2;
- float d;
- vec_t length;
- int counts[3];
- bool fliptest;
-
- /* check all combinations */
- for (i = 0; i < source->numpoints; i++) {
- l = (i + 1) % source->numpoints;
- VectorSubtract(source->points[l], source->points[i], v1);
-
- /*
- * fing a vertex of pass that makes a plane that puts all of the
- * vertexes of pass on the front side and all of the vertexes of
- * source on the back side
- */
- for (j = 0; j < pass->numpoints; j++) {
- VectorSubtract(pass->points[j], source->points[i], v2);
-
- plane.normal[0] = v1[1] * v2[2] - v1[2] * v2[1];
- plane.normal[1] = v1[2] * v2[0] - v1[0] * v2[2];
- plane.normal[2] = v1[0] * v2[1] - v1[1] * v2[0];
-
- /* if points don't make a valid plane, skip it */
- length = plane.normal[0] * plane.normal[0]
- + plane.normal[1] * plane.normal[1]
- + plane.normal[2] * plane.normal[2];
-
- if (length < ON_EPSILON)
- continue;
-
- length = 1 / sqrt(length);
-
- plane.normal[0] *= length;
- plane.normal[1] *= length;
- plane.normal[2] *= length;
-
- plane.dist = DotProduct(pass->points[j], plane.normal);
-
- /*
- * find out which side of the generated seperating plane has the
- * source portal
- */
- fliptest = FALSE;
- for (k = 0; k < source->numpoints; k++) {
- if (k == i || k == l)
- continue;
- d = DotProduct(source->points[k], plane.normal) - plane.dist;
- if (d < -ON_EPSILON) { /* source is on the negative side, so we want all */
- /* pass and target on the positive side */
- fliptest = FALSE;
- break;
- }
- else if (d > ON_EPSILON) { /* source is on the positive side, so we want all */
- /* pass and target on the negative side */
- fliptest = TRUE;
- break;
- }
- }
- if (k == source->numpoints)
- continue; /* planar with source portal */
-
- /* flip the normal if the source portal is backwards */
- if (fliptest) {
- VectorNegate(plane.normal);
- plane.dist = -plane.dist;
- }
-
- /*
- * if all of the pass portal points are now on the positive side,
- * this is the seperating plane
- */
- counts[0] = counts[1] = counts[2] = 0;
- for (k = 0; k < pass->numpoints; k++) {
- if (k == j)
- continue;
- d = DotProduct(pass->points[k], plane.normal) - plane.dist;
- if (d < -ON_EPSILON)
- break;
- else if (d > ON_EPSILON)
- counts[0]++;
- else
- counts[2]++;
- }
- if (k != pass->numpoints)
- continue; /* points on negative side, not a seperating plane */
-
- if (!counts[0]) {
- continue; /* planar with seperating plane */
-
- }
-
- /* flip the normal if we want the back side */
- if (flipclip) {
- VectorNegate(plane.normal);
- plane.dist = -plane.dist;
- }
-
- /* clip target by the seperating plane */
- target = ClipWinding(target, &plane, FALSE);
- if (!target)
- return NULL; /* target is not visible */
-
- }
- }
-
- return target;
- }
- #else
- struct winding *ClipToSeperators(struct winding *SourceWinding, struct winding *PassWinding,
- struct winding *TargetWinding, bool flipclip)
- {
- int i, j, k, l;
-
- struct plane ClipPlane;
- vec3_t v1, v2;
- float d;
- double length;
-
- bool ContFlag;
- bool fliptest;
-
- /*
- * Zero'th idea: Only one j point can make a candidate plane. (RVis).
- *
- * First idea: A good candidate for the next point, would be the same
- * point. ::: Didn't show up as an improvement.
- *
- * Second idea: If one combination of the points gives a planar test
- * agains the source portal, all points will (approximatly. Some might
- * not (the ON_EPSILON 'error'), but the planes constructed from this
- * is either behind the PassWinding (and will therefore not interfere with
- * the TargetWinding), or on the same side of PassWinding as the SourceWinding.
- * (In the last case, the plane will be rejected in any case).
- * I don't know if this logic is OK....
- * :::: Very very bad idea. Time went up, correctness went down.
- * Logic must have been broke....
- */
-
- /* check all combinations */
- for (i = 0; i < SourceWinding->numpoints; i++) {
- l = (i + 1) % SourceWinding->numpoints;
- /* We now have two points. Make a vector from them. (v1). */
- VectorSubtract(SourceWinding->points[l], SourceWinding->points[i], v1);
-
- /*
- * Find a vertex of PassWinding that makes a ClipPlane that puts all of the
- * vertexes of PassWinding on the front side and all of the vertexes of
- * SourceWinding on the back side
- */
- for (j = 0; (j < PassWinding->numpoints); j++) {
- /* Set j. Overflow rightly. */
- /* j = (Next + Count)%PassWinding->numpoints; */
-
- /*
- * Make another vector from one point, and this point.
- * The two vectors now defines a plane.
- */
- VectorSubtract(PassWinding->points[j], SourceWinding->points[i], v2);
-
- /* Define a plane normal, with the crossproduct. */
- ClipPlane.normal[0] = v1[1] * v2[2] - v1[2] * v2[1];
- ClipPlane.normal[1] = v1[2] * v2[0] - v1[0] * v2[2];
- ClipPlane.normal[2] = v1[0] * v2[1] - v1[1] * v2[0];
-
- /*
- * if points don't make a valid ClipPlane, skip it
- * Get the normals lenght
- */
- length = ClipPlane.normal[0] * ClipPlane.normal[0]
- + ClipPlane.normal[1] * ClipPlane.normal[1]
- + ClipPlane.normal[2] * ClipPlane.normal[2];
- /* If too small, skip it. */
- if (length < ON_EPSILON)
- continue;
-
- /* Normalize the plane. */
- length = 1 / sqrt(length);
- ClipPlane.normal[0] *= length;
- ClipPlane.normal[1] *= length;
- ClipPlane.normal[2] *= length;
-
- ClipPlane.dist = DotProduct(PassWinding->points[j], ClipPlane.normal);
-
- /*
- * find out which side of the generated seperating ClipPlane has the
- * SourceWinding portal
- */
- fliptest = FALSE;
- for (k = 0; k < SourceWinding->numpoints; k++) {
- if (k == i || k == l)
- continue;
- d = DotProduct(SourceWinding->points[k], ClipPlane.normal) - ClipPlane.dist;
- if (d < -ON_EPSILON) { /* SourceWinding is on the negative side, so we want all */
- /* PassWinding and TargetWinding on the positive side */
- fliptest = FALSE;
- break;
- }
- else if (d > ON_EPSILON) { /* SourceWinding is on the positive side, so we want all */
- /* PassWinding and TargetWinding on the negative side */
- fliptest = TRUE;
- break;
- }
- }
- if (k == SourceWinding->numpoints)
- continue; /* planar with SourceWinding portal */
-
- /* flip the normal if the SourceWinding portal is backwards */
- if (fliptest) {
- VectorSubtract(vec3_origin, ClipPlane.normal, ClipPlane.normal);
- ClipPlane.dist = -ClipPlane.dist;
- }
-
- /*
- * if all of the PassWinding portal points are now on the positive side,
- * this is the seperating ClipPlane
- *
- * We want t0 calculate further, if no points behind, and at least one in front
- */
- ContFlag = TRUE;
-
- for (k = 0; k < PassWinding->numpoints; k++) {
- if (k == j)
- continue;
- d = DotProduct(PassWinding->points[k], ClipPlane.normal) - ClipPlane.dist;
- if (d < -ON_EPSILON) {
- ContFlag = TRUE;
- break;
- }
- /* If at least one point is above the plane, then */
- /* we can calculate. */
- else if (d > ON_EPSILON) {
- ContFlag = FALSE;
- }
- }
- /*
- * We want to calculate further, if
- * a) No points with d < -ON_EPSILON
- * b) At least one point with non planar.
- */
- if (ContFlag)
- continue;
-
- /* flip the normal if we want the back side */
- if (flipclip) {
- VectorSubtract(vec3_origin, ClipPlane.normal, ClipPlane.normal);
- ClipPlane.dist = -ClipPlane.dist;
- }
-
- /* clip TargetWinding by the seperating ClipPlane */
- TargetWinding = ClipWinding(TargetWinding, &ClipPlane, FALSE);
- if (!TargetWinding)
- return NULL; /* TargetWinding is not visible */
- /* RVIS modification.
- *
- * Observation:
- * For any given vector, beeing part of the SourceWinding, at most
- * one point in PassWinding, together with the vector, defines a uniqe
- * plane with all points of SourceWinding on one side, and all
- * points of PassWinding at the other.
- * There may be other points, that fullfill the criteria, but
- * the plane generated will be the same.
- * Therefore, further search is fruitless.
- */
- break;
- } /* j For loop. */
- } /* i for loop */
- return TargetWinding;
- }
- #endif
-
- /*
- * ==================
- * RecursiveLeafFlow
- *
- * Flood fill through the leafs
- * If src_portal is NULL, this is the originating leaf
- * ==================
- *
- * Called from PortalFlow.
- * This is a huge procedure, and probably the one that contains all the meat
- * of the program.
- * In here, the testlevel parameter is used.
- * ClipToSeperators are called testlevel times, with different parameters.
- */
- void RecursiveLeafFlow(register int leafnum, register threaddata_t * thread, register pstack_t * prevstack)
- {
- pstack_t stack;
- struct visportal *p;
- struct plane backplane;
- struct winding *source, *target;
- struct visleaf *leaf;
- int i, j;
-
- /*
- * only on some systems "long int" is 64bits,
- * but on all "long long int" is 64bits
- */
- long long int *test, *might, *vis;
- bool more;
-
- c_chains++;
-
- leaf = leafs[leafnum];
- CheckStack(leaf, thread);
-
- /* mark the leaf as visible */
- if (!(thread->leafvis[leafnum >> 3] & (1 << (leafnum & 7)))) {
- thread->leafvis[leafnum >> 3] |= 1 << (leafnum & 7);
- thread->base->numcansee++;
- }
-
- prevstack->next = &stack;
- stack.next = NULL;
- stack.leaf = leaf;
- stack.portal = NULL;
- if (!(stack.mightsee = (unsigned char *)tmalloc(bitbytes)))
- Error(failed_memoryunsize, "bitbytes");
- might = (long long int *)stack.mightsee;
- vis = (long long int *)thread->leafvis;
-
- /* check all portals for flowing into other leafs */
- for (i = 0; i < leaf->numportals; i++) {
- p = leaf->portals[i];
-
- if (!(prevstack->mightsee[p->leaf >> 3] & (1 << (p->leaf & 7)))) {
- c_leafskip++;
- continue; /* can't possibly see it */
-
- }
-
- /* if the portal can't see anything we haven't allready seen, skip it */
- if (p->status == stat_done) {
- c_vistest++;
- test = (long long int *)p->visbits;
- }
- else {
- c_mighttest++;
- test = (long long int *)p->mightsee;
- }
- more = FALSE;
- for (j = 0; j < bitlongs; j++) {
- might[j] = ((long long int *)prevstack->mightsee)[j] & test[j];
- if (might[j] & ~vis[j])
- more = TRUE;
- }
-
- if (!more) { /* can't see anything new */
-
- c_portalskip++;
- continue;
- }
-
- /* get plane of portal, point normal into the neighbor leaf */
- stack.portalplane = p->plane;
- VectorNegateTo(p->plane.normal, backplane.normal);
- backplane.dist = -p->plane.dist;
-
- if (VectorCompare(prevstack->portalplane.normal, backplane.normal))
- continue; /* can't go out a coplanar face */
-
- c_portalcheck++;
-
- stack.portal = p;
- stack.next = NULL;
-
- target = ClipWinding(p->winding, &thread->pstack_head.portalplane, FALSE);
- if (!target)
- continue;
-
- if (!prevstack->pass) { /* the second leaf can only be blocked if coplanar */
- stack.source = prevstack->source;
- stack.pass = target;
- RecursiveLeafFlow(p->leaf, thread, &stack);
- FreeWinding(target);
- continue;
- }
-
- target = ClipWinding(target, &prevstack->portalplane, FALSE);
- if (!target)
- continue;
-
- source = CopyWinding(prevstack->source);
- source = ClipWinding(source, &backplane, FALSE);
- if (!source) {
- FreeWinding(target);
- continue;
- }
-
- c_portaltest++;
-
- if (testvislevel > 0) {
- target = ClipToSeperators(source, prevstack->pass, target, FALSE);
- if (!target) {
- FreeWinding(source);
- continue;
- }
- }
-
- if (testvislevel > 1) {
- target = ClipToSeperators(prevstack->pass, source, target, TRUE);
- if (!target) {
- FreeWinding(source);
- continue;
- }
- }
-
- if (testvislevel > 2) {
- source = ClipToSeperators(target, prevstack->pass, source, FALSE);
- if (!source) {
- FreeWinding(target);
- continue;
- }
- }
-
- if (testvislevel > 3) {
- source = ClipToSeperators(prevstack->pass, target, source, TRUE);
- if (!source) {
- FreeWinding(target);
- continue;
- }
- }
-
- /*
- * The reason why, this level, and > 3 is beeing run, is because "non-aligned"
- * portals will generate different planes
- * This also means, that "aligned" portals, need not run this.
- * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
- * Since a great deal IS aligned, it might be worth trying!!!
- */
- stack.source = source;
- stack.pass = target;
-
- c_portalpass++;
-
- /* flow through it for real */
- RecursiveLeafFlow(p->leaf, thread, &stack);
-
- FreeWinding(source);
- FreeWinding(target);
- }
-
- tfree(stack.mightsee);
- }
-
- /*
- * ===============
- * PortalFlow
- *
- * ===============
- */
- void PortalFlow(register struct visportal *p)
- {
- threaddata_t data;
-
- if (p->status != stat_working)
- Error("PortalFlow: reflowed\n");
- p->status = stat_working;
-
- if (!(p->visbits = (unsigned char *)kmalloc(bitbytes)))
- Error(failed_memoryunsize, "bitbytes");
-
- __bzero(&data, sizeof(data));
- data.leafvis = p->visbits;
- data.base = p;
-
- data.pstack_head.portal = p;
- data.pstack_head.source = p->winding;
- data.pstack_head.portalplane = p->plane;
- data.pstack_head.mightsee = p->mightsee;
-
- RecursiveLeafFlow(p->leaf, &data, &data.pstack_head);
-
- p->status = stat_done;
- }
-
- /*
- * ===============================================================================
- * This is a rough first-order aproximation that is used to trivially reject some
- * of the final calculations.
- * ===============================================================================
- *
- * This procedure flows out of a source leaf entering from portal scrportal.
- * First, the portal's mightsee array is set to 1, for the leafnum leaf
- * Then, for each portal leaving the leaf, it is checked if the srcportal can see
- * this portal. If that is the case, the procedure is called recursively.
- */
-
- void SimpleFlood(register struct visportal *srcportal, register int leafnum)
- {
- int i;
- struct visleaf *leaf;
- struct visportal *p;
-
- /* Catchs circular references. */
- if (srcportal->mightsee[leafnum >> 3] & (1 << (leafnum & 7)))
- return;
- /* Set the current leaf to visible. */
- srcportal->mightsee[leafnum >> 3] |= (1 << (leafnum & 7));
- c_leafsee++;
-
- /* Get the portals leaving this leaf. */
- leaf = leafs[leafnum];
-
- /* Check each portal away from leaf */
- for (i = 0; i < leaf->numportals; i++) {
- p = leaf->portals[i];
- /* If the portal is visible from srcportal */
- if (!portalsee[p - portals])
- continue;
- /* then call recursivly. */
- SimpleFlood(srcportal, p->leaf);
- }
- }
-
- /*
- * ==============
- * BasePortalVis
- * ==============
- *
- * It performs a trivial reject of some portal connections.
- * The criteria is, that one portal has to be able to look through, in the
- * loosest sense, another portal.
- * In effect, this means, that portal b need to be in front of a, and portal
- * a need to be behind b.
- */
- #ifndef NEWVIS
- #define BasePortalVis_1 BasePortalVis
- #define BasePortalVis_2 BasePortalVis
- #define BasePortalVis_3 BasePortalVis
- #define BasePortalVis_4 BasePortalVis
- void BasePortalVis(void)
- {
- int i, j, k;
- struct visportal *tp, *p;
- float d;
- struct winding *w;
-
- for (i = 0, p = portals; i < num_visportals * 2; i++, p++) {
- if (!(p->mightsee = (unsigned char *)kmalloc(bitbytes)))
- Error(failed_memoryunsize, "bitbytes");
- c_portalsee = 0;
- __bzero(portalsee, num_visportals * 2);
-
- for (j = 0, tp = portals; j < num_visportals * 2; j++, tp++) {
- if (j == i)
- continue;
- w = tp->winding;
- for (k = 0; k < w->numpoints; k++) {
- d = DotProduct(w->points[k], p->plane.normal) - p->plane.dist;
- if (d > ON_EPSILON)
- break;
- }
- if (k == w->numpoints)
- continue; /* no points on front */
-
- w = p->winding;
- for (k = 0; k < w->numpoints; k++) {
- d = DotProduct(w->points[k], tp->plane.normal) - tp->plane.dist;
- if (d < -ON_EPSILON)
- break;
- }
- if (k == w->numpoints)
- continue; /* no points on front */
-
- portalsee[j] = 1;
- c_portalsee++;
-
- }
-
- c_leafsee = 0;
- SimpleFlood(p, p->leaf);
- p->nummightsee = c_leafsee;
- }
- }
- #else
- /*
- * PortalFrontPortal returns true if at least one of the points of TestPortals
- * windings is in front if SourcePortal
- */
- bool PortalFrontBackPortal_3(struct visportal *SourcePortal, struct visportal *TestPortal)
- {
- int i;
- float d;
- struct winding *Winding;
-
- /* Test front */
- Winding = TestPortal->winding;
- for (i = 0; i < Winding->numpoints; i++) {
- d = DotProduct(Winding->points[i], SourcePortal->plane.normal)
- - SourcePortal->plane.dist;
- if (d > ON_EPSILON) /* Test if back */
- break;
- }
- if (i == Winding->numpoints)
- return FALSE;
- /* Test back */
- Winding = SourcePortal->winding;
- for (i = 0; i < Winding->numpoints; i++) {
- d = DotProduct(Winding->points[i], TestPortal->plane.normal)
- - TestPortal->plane.dist;
- if (d < -ON_EPSILON)
- return TRUE;
- }
- return FALSE;
- } /* PortalFrontPortal */
-
- /*
- * 4:
- * This version of BasePortalVis uses a DFS approach to find the
- * mightsee set.
- * Besides that, it also uses a stronger test-criterium to lower
- * the upper limit recorded in the mightsee set.
- */
- /* For now; allocate a hell of a lot of pointers; */
-
- struct visportal *ThroughPortals[MAX_MAP_PORTALS];
- int ThroughCount;
-
- /* Returns true, if NOT in array. */
- bool PortalCircularCheck(struct visportal *TestPortal)
- {
- int k;
-
- for (k = 0; k < ThroughCount; k++)
- if (ThroughPortals[k] == TestPortal)
- return FALSE;
- return TRUE;
- }
-
- /* Test TestPortal with all portals in ThroughPortals */
- bool PortalThroughPortals_4(struct visportal * TestPortal)
- {
- int k;
-
- for (k = 0; k < ThroughCount; k++)
- if (!(PortalFrontBackPortal_3(ThroughPortals[k], TestPortal)))
- return FALSE;
- return TRUE;
- }
-
- /*
- * PortalThroughLeaf_4 returns TRUE, IF all portals leaving leafnum,
- * does not fullfill the standard test criterium.
- */
- void PortalThroughLeaf_4(int leafnum, bool LastOut)
- {
- int i;
-
- struct visleaf *leaf;
- struct visportal *TestPortal;
- bool IsLast;
-
- /* Catchs circular references. */
- if (ThroughPortals[0]->mightsee[leafnum >> 3] & (1 << (leafnum & 7)))
- return;
- /* Set the current leaf to visible. */
- ThroughPortals[0]->mightsee[leafnum >> 3] |= (1 << (leafnum & 7));
- c_leafsee++;
- /* Get the portals leaving this leaf. */
- leaf = &leafs[leafnum];
- for (i = 0; i < leaf->numportals; i++) { /* Check each portal away from leaf */
- TestPortal = leaf->portals[i];
- if (LastOut) { /* Can use strict testing. */
- if (PortalThroughPortals_4(TestPortal)) {
- if (i == (leaf->numportals - 1))
- IsLast = TRUE;
- else
- IsLast = FALSE;
- ThroughPortals[ThroughCount++] = TestPortal;
- PortalThroughLeaf_4(TestPortal->leaf, IsLast); /* then call recursivly. */
- ThroughCount--;
- }
- }
- else { /* Can not use strict testing. */
- if (PortalFrontBackPortal_3(ThroughPortals[0], TestPortal)) {
- ThroughPortals[ThroughCount++] = TestPortal;
- PortalThroughLeaf_4(TestPortal->leaf, FALSE);
- ThroughCount--;
- }
- }
- } /* For each portal */
- } /* PortalThroughLeaf(...) */
-
- /* Simply call PortalFollowPortals for each portal. */
- void BasePortalVis_4(void)
- {
- int i;
- struct visportal *SourcePortal;
-
- /* For each portal repeat */
- for (i = 0, SourcePortal = portals; i < numportals * 2; i++, SourcePortal++) {
- /* Allocate memory to the mightsee array */
- SourcePortal->mightsee = kmalloc(bitbytes);
- __bzero(SourcePortal->mightsee, bitbytes);
- /* SimpleFloof tags all the leafs, that SourcePortal might see. */
- ThroughPortals[0] = SourcePortal;
- ThroughCount = 1;
- c_leafsee = 0;
- PortalThroughLeaf_4(SourcePortal->leaf, TRUE);
- SourcePortal->nummightsee = c_leafsee;
- } /* For each portal */
- }
-
- /*
- * 3:
- * This version of BasePortalVis uses a DFS approach to find the
- * mightsee set.
- * It thereby hopes, that it can perform fewer calculations than
- * the standard version. It could result in -worse- performance,
- * but so far, it has been running in 35-55% of original time.
- * It uses a helper funtion, that appears first:
- */
- void PortalThroughLeaf_3(struct visportal *SourcePortal, int leafnum)
- {
- int i;
- struct visleaf *leaf;
- struct visportal *TestPortal;
-
- /* Catchs circular references. */
- if (SourcePortal->mightsee[leafnum >> 3] & (1 << (leafnum & 7)))
- return;
- /* Set the current leaf to visible. */
- SourcePortal->mightsee[leafnum >> 3] |= (1 << (leafnum & 7));
- c_leafsee++;
- /* Get the portals leaving this leaf. */
- leaf = &leafs[leafnum];
- for (i = 0; i < leaf->numportals; i++) { /* Check each portal away from leaf */
- TestPortal = leaf->portals[i];
- if (PortalFrontBackPortal_3(SourcePortal, TestPortal))
- PortalThroughLeaf_3(SourcePortal, TestPortal->leaf); /* then call recursivly. */
- }
- } /* PortalThroughLeaf(...) */
-
- /* Simply call PortalFollowPortals for each portal. */
- void BasePortalVis_3(void)
- {
- int i;
- struct visportal *SourcePortal; /*, EndPortal; */
-
- /* For each portal repeat */
- for (i = 0, SourcePortal = portals; i < numportals * 2; i++, SourcePortal++) {
- /* Allocate memory to the mightsee array */
- SourcePortal->mightsee = kmalloc(bitbytes);
- __bzero(SourcePortal->mightsee, bitbytes);
- c_leafsee = 0;
- /* SimpleFloof tags all the leafs, that SourcePortal might see. */
- PortalThroughLeaf_3(SourcePortal, SourcePortal->leaf);
- SourcePortal->nummightsee = c_leafsee;
- } /* For each portal */
-
- }
-
- /*
- * 2:
- * This is the first modified version of baseportal vis.
- * Some algoritmic improvements, based on the observation, that for i even, the
- * points of the windings of portal i and i+1 are the same.
- * It runs in approc 66-75% of the time the standard needs.
- *
- */
- void BasePortalVis_2(void)
- { /* Faster then standard, one row, same result */
- int i, j, k;
- struct visportal *TestPortal, *SourcePortal;
- float d;
- struct winding *w;
- bool TP1, TP2;
-
- /*
- * For each global Portal, we check if it might be able to see TestPortal
- * Information is stored in the portalsee array.
- */
- for (i = 0, SourcePortal = portals; i < numportals * 2; i++, SourcePortal++) {
- c_portalsee = 0; /* This is how many other portals, p can see. */
- __bzero(portalsee, numportals * 2); /* We clear the array. */
-
- /*
- * For each other portal, test if SourcePortal might be able to see it.
- * Very loose bound. Front/Back
- * Since the points of j and j+1 are the same, when j are even, some
- * calculations can be spared.
- */
- for (j = 0, TestPortal = portals;
- j < numportals * 2;
- j++, j++, TestPortal++, TestPortal++) {
- if (i == j) /* This only catches i even, i=j and i=j+1 */
- continue;
- /* First, check if any point from TestPortal is in front of SourcePortal */
- w = TestPortal->winding;
- for (k = 0; k < w->numpoints; k++) {
- d = DotProduct(w->points[k], SourcePortal->plane.normal) - SourcePortal->plane.dist;
- if (d > ON_EPSILON)
- break;
- }
- if (k == w->numpoints) {
- /* Same points, so same test will fail. */
- continue; /* no points on front */
- }
- /*
- * If we reached here, for j = x, j even, then we are also
- * going to reach this point for j = x + 1, since same points.
- * And vice versa, the other way around. If we did NOT reach here,
- * for j = x, j even, we are NOT going to get here for j = x + 1
- *
- * Reaching this point, means that SourcePortal can see either
- * TestPortal, or TestPortal++
- */
- /* Now check, if SourcePortal is behind TestPortal */
- TP1 = TP2 = FALSE;
- w = SourcePortal->winding;
- for (k = 0; k < w->numpoints; k++) {
- d = DotProduct(w->points[k], TestPortal->plane.normal) - TestPortal->plane.dist;
- if (d < -ON_EPSILON)
- TP1 = TRUE;
- else if (d > ON_EPSILON)
- TP2 = TRUE;
- if (TP1 && TP2)
- break;
- }
- if (TP1) {
- portalsee[j] = 1;
- c_portalsee++;
- }
- /* We never want to set the portal to see it self. */
- if (i == j + 1)
- continue; /* This catches the odd i's, with i == j */
- /* Check, if SourcePortal is behind TestPortal++ */
-
- if (TP2) {
- portalsee[j + 1] = 1;
- c_portalsee++;
- }
- } /* End of TestPortal loop. */
-
- c_leafsee = 0; /* */
- /* Allocate space for the mightsee array. Filled by SimpleFlood */
- SourcePortal->mightsee = kmalloc(bitbytes);
- __bzero(SourcePortal->mightsee, bitbytes);
- /* SimpleFloof tags all the leafs, that SourcePortal might see. */
- SimpleFlood(SourcePortal, SourcePortal->leaf);
- SourcePortal->nummightsee = c_leafsee;
- }
- }
-
- /* This is the standard, but commented, version of BasePortalVis */
- void BasePortalVis_1(void)
- { /* Standard */
- int i, j, k;
- struct visportal *tp, *p;
- float d;
- struct winding *w;
-
- /* portals are the global portal array */
- for (i = 0, p = portals; i < numportals * 2; i++, p++) {
- p->mightsee = kmalloc(bitbytes);
- __bzero(p->mightsee, bitbytes);
-
- /* portalsee is delared in this file. */
- c_portalsee = 0; /* This is how many other portals, p can see. */
- __bzero(portalsee, numportals * 2); /* We clear the array. */
-
- for (j = 0, tp = portals; j < numportals * 2; j++, tp++) {
- if (j == i)
- continue;
- w = tp->winding;
- /*
- * This for loop, apparently, checks if there is a point on the tp
- * winding, that is in front of the p plane.
- */
- for (k = 0; k < w->numpoints; k++) {
- d = DotProduct(w->points[k], p->plane.normal) - p->plane.dist;
- if (d > ON_EPSILON)
- break;
- }
- if (k == w->numpoints)
- continue; /* no points on front */
- /*
- * The skipping here, must be because we have determined, that
- * no points of the portal we checked against lies in 'the direction' in
- * which we look.
- *
- * Same check for the other way around.
- * No! This check must be for be for BEHIND!
- */
- w = p->winding;
- for (k = 0; k < w->numpoints; k++) {
- d = DotProduct(w->points[k], tp->plane.normal) - tp->plane.dist;
- if (d < -ON_EPSILON)
- break;
- }
- if (k == w->numpoints)
- continue; /* no points on front <== Must be BACK! */
-
- /* If we reached this far, portal p (i) can se portal tp (j) */
- portalsee[j] = 1;
- c_portalsee++;
-
- }
- c_leafsee = 0; /* */
- SimpleFlood(p, p->leaf);
- p->nummightsee = c_leafsee;
- }
- }
- #endif
-
- /*
- *
- * Some textures (sky, water, slime, lava) are considered ambien sound emiters.
- * Find an aproximate distance to the nearest emiter of each class for each leaf.
- *
- */
-
- /*
- * ====================
- * SurfaceBBox
- *
- * ====================
- */
- void SurfaceBBox(__memBase, register struct dface_t *s, register vec3_t mins, register vec3_t maxs)
- {
- int i;
- short int j;
- int e;
- int vi;
- float *v;
-
- mins[0] = mins[1] = 999999;
- maxs[0] = maxs[1] = -99999;
-
- for (i = 0; i < s->numedges; i++) {
- e = bspMem->shared.quake1.dsurfedges[s->firstedge + i];
- if (e >= 0)
- vi = bspMem->shared.quake1.dedges[e].v[0];
- else
- vi = bspMem->shared.quake1.dedges[-e].v[1];
- v = bspMem->shared.quake1.dvertexes[vi].point;
-
- for (j = 0; j < 3; j++) {
- if (v[j] < mins[j])
- mins[j] = v[j];
- if (v[j] > maxs[j])
- maxs[j] = v[j];
- }
- }
- }
-
- /*
- * ====================
- * CalcAmbientSounds
- *
- * ====================
- */
- void CalcAmbientSounds(__memBase)
- {
- int i, j, k;
- short int l;
- struct dleaf_t *leaf, *hit;
- unsigned char *vis;
- struct dface_t *surf;
- vec3_t mins, maxs;
- float d, maxd;
- int ambient_type;
- struct texinfo *info;
- struct mipmap *miptex;
- int ofs;
- float dists[NUM_AMBIENTS];
- float vol;
-
- for (i = 0; i < num_visleafs; i++) {
- leaf = &bspMem->shared.quake1.dleafs[i + 1];
-
- /* clear ambients */
- for (j = 0; j < NUM_AMBIENTS; j++)
- dists[j] = 1020;
-
- vis = &uncompressed[i * bitbytes];
-
- for (j = 0; j < num_visleafs; j++) {
- if (!(vis[j >> 3] & (1 << (j & 7))))
- continue;
-
- /* check this leaf for sound textures */
- hit = &bspMem->shared.quake1.dleafs[j + 1];
-
- for (k = 0; k < hit->nummarksurfaces; k++) {
- surf = &bspMem->shared.quake1.dfaces[bspMem->shared.quake1.dmarksurfaces[hit->firstmarksurface + k]];
- info = &bspMem->shared.quake1.texinfo[surf->texinfo];
- ofs = ((struct dmiptexlump_t *)bspMem->shared.quake1.dtexdata)->dataofs[info->miptex];
- miptex = (struct mipmap *)(&bspMem->shared.quake1.dtexdata[ofs]);
-
- if (!strncasecmp(miptex->name, "sky", 3))
- ambient_type = AMBIENT_SKY;
- else if (!strncasecmp(miptex->name, "*slime", 6))
- ambient_type = AMBIENT_SLIME; /* AMBIENT_SLIME; */
- else if (!strncasecmp(miptex->name, "*lava", 6))
- ambient_type = AMBIENT_LAVA;
- else if (miptex->name[0] == '*')
- ambient_type = AMBIENT_WATER;
- else
- continue;
-
- /* find distance from source leaf to polygon */
- SurfaceBBox(bspMem, surf, mins, maxs);
- maxd = 0;
- for (l = 0; l < 3; l++) {
- if (mins[l] > leaf->maxs[l])
- d = mins[l] - leaf->maxs[l];
- else if (maxs[l] < leaf->mins[l])
- d = leaf->mins[l] - mins[l];
- else
- d = 0;
- if (d > maxd)
- maxd = d;
- }
-
- maxd = 0.25;
- if (maxd < dists[ambient_type])
- dists[ambient_type] = maxd;
- }
- }
-
- for (j = 0; j < NUM_AMBIENTS; j++) {
- if (dists[j] < 100)
- vol = 1.0;
- else {
- vol = 1.0 - dists[2] * 0.002;
- if (vol < 0)
- vol = 0;
- }
- leaf->ambient_level[j] = vol * 255;
- }
- mprogress(num_visleafs, i + 1);
- }
- }
-
- /*============================================================================= */
-
- /*
- * =============
- * GetNextPortal
- *
- * Returns the next portal for a thread to work on
- * Returns the portals from the least complex, so the later ones can reuse
- * the earlier information.
- * =============
- */
- struct visportal *GetNextPortal(void)
- {
- int j;
- struct visportal *p, *tp;
- int min;
-
- min = 99999;
- p = NULL;
-
- /*
- * Finds the portal with the minimum mightsee number.
- * This operation is probably done quite a few times.
- * So maybe a data structure to update this information
- * would be appropiate? (min heap?)
- * Also, all portals are seeked. If there is no way a portal
- * can stop having status = stat_done, then it would be faster
- * to have another datastructure.
- */
- for (j = 0, tp = portals; j < num_visportals * 2; j++, tp++) {
- if (tp->nummightsee < min && tp->status == stat_none) {
- min = tp->nummightsee;
- p = tp;
- }
- }
-
- if (p)
- p->status = stat_working;
-
- return p;
- }
-
- /*
- * ===============
- * CompressRow
- *
- * ===============
- */
- int CompressRow(register unsigned char *vis, register unsigned char *dest)
- {
- int j;
- int rep;
- int visrow;
- unsigned char *dest_p;
-
- dest_p = dest;
- visrow = (num_visleafs + 7) >> 3;
-
- for (j = 0; j < visrow; j++) {
- *dest_p++ = vis[j];
- if (vis[j])
- continue;
-
- rep = 1;
- for (j++; j < visrow; j++)
- if (vis[j] || rep == 255)
- break;
- else
- rep++;
- *dest_p++ = rep;
- j--;
- }
-
- return dest_p - dest;
- }
-
- /*
- * =============
- * SortPortals
- *
- * Sorts the portals from the least complex, so the later ones can reuse
- * the earlier information.
- * =============
- */
- /*
- * int PComp(register struct visportal *a, register struct visportal *b)
- * {
- * if (a->nummightsee == b->nummightsee)
- * return 0;
- * if (a->nummightsee < b->nummightsee)
- * return -1;
- * return 1;
- * }
- *
- * void SortPortals(void)
- * {
- * heapsort(portals, num_visportals * 2, sizeof(struct visportal), PComp);
- * }
- */
-
- /*
- * ===============
- * LeafFlow
- *
- * Builds the entire visibility list for a leaf
- * ===============
- */
- void LeafFlow(__memBase, register int leafnum)
- {
- struct visleaf *leaf;
- unsigned char *outbuffer;
- unsigned char compressed[MAX_MAP_LEAFS / 8];
- int i, j;
- int numvis;
- struct visportal *p;
-
- /* flow through all portals, collecting visible bits */
- outbuffer = uncompressed + leafnum * bitbytes;
- /* Get a pointer to the leafnum leaf */
- leaf = leafs[leafnum];
- /* Iterate over all portals leaving this leaf. */
- for (i = 0; i < leaf->numportals; i++) {
- p = leaf->portals[i];
- if (p->status != stat_done)
- Error("portal not done\n");
- /* Or the leafs visible from this portal to the outbuffer */
- for (j = 0; j < bitbytes; j++)
- outbuffer[j] |= p->visbits[j];
- }
-
- /* Checks if the source leaf is visible from this leaf. */
- if (outbuffer[leafnum >> 3] & (1 << (leafnum & 7)))
- Error("Leaf portals saw into leaf\n");
-
- /* Writes the leaf itself to the visible array. */
- outbuffer[leafnum >> 3] |= (1 << (leafnum & 7));
-
- /* Collects statistical info. */
- numvis = 0;
- for (i = 0; i < num_visleafs; i++)
- if (outbuffer[i >> 3] & (1 << (i & 3)))
- numvis++;
-
- /* compress the bit string */
- if (bspMem->visOptions & VIS_VERBOSE)
- mprintf("----- leaf %4i ---------\n%5i visible\n", leafnum, numvis);
- totalvis += numvis;
-
- /* Compress the row to the "compressed" array. */
- i = CompressRow(outbuffer, compressed);
-
- if ((bspMem->shared.quake1.visdatasize + i) > bspMem->shared.quake1.max_visdatasize)
- ExpandClusters(bspMem, LUMP_VISIBILITY);
- memcpy(bspMem->shared.quake1.dvisdata + bspMem->shared.quake1.visdatasize, compressed, i);
- bspMem->shared.quake1.dleafs[leafnum + 1].visofs = bspMem->shared.quake1.visdatasize; /* leaf 0 is a common solid */
- bspMem->shared.quake1.visdatasize += i;
- }
-
- /*
- * ==================
- * CalcPortalVis
- * ==================
- */
- void CalcPortalVis(__memBase)
- {
- int i;
- struct visportal *p;
-
- /* fastvis just uses mightsee for a very loose bound */
- if (bspMem->visOptions & VIS_FAST) {
- for (i = 0; i < num_visportals * 2; i++) {
- portals[i].visbits = portals[i].mightsee;
- portals[i].status = stat_done;
- }
- return;
- }
-
- leafon = 0;
-
- while ((p = GetNextPortal())) {
- PortalFlow(p);
- if (bspMem->visOptions & VIS_VERBOSE)
- mprintf("----- portal %4i -------\n %5i mightsee\n%5i cansee\n", (int)(p - portals), p->nummightsee, p->numcansee);
- }
-
- if (bspMem->visOptions & VIS_VERBOSE) {
- mprintf("%5i portalcheck\n%5i portaltest\n%5i portalpass\n", c_portalcheck, c_portaltest, c_portalpass);
- mprintf("%5i c_vistest\n%5i c_mighttest\n", c_vistest, c_mighttest);
- }
-
- }
-
- /*
- * ==================
- * CalcVis
- * ==================
- */
- void CalcVis(__memBase)
- {
- int i;
-
- switch (testvisfastlevel) {
- case 1:
- BasePortalVis_1();
- break;
- case 2:
- BasePortalVis_2();
- break;
- case 3:
- BasePortalVis_3();
- break;
- case 4:
- BasePortalVis_4();
- break;
- default:
- BasePortalVis_3();
- break;
- }
-
- /*SortPortals(); */
- CalcPortalVis(bspMem);
-
- /* assemble the leaf vis lists by oring and compressing the portal lists */
- for (i = 0; i < num_visleafs; i++) {
- LeafFlow(bspMem, i);
- mprogress(num_visleafs, i + 1);
- }
-
- mprintf("%5i average leafs visible\n", totalvis / num_visleafs);
- }
-
- /*
- * ==============================================================================
- *
- * PASSAGE CALCULATION (not used yet...)
- *
- * ==============================================================================
- */
-
- bool PlaneCompare(register struct plane *p1, register struct plane *p2)
- {
- int i;
-
- if (fabs(p1->dist - p2->dist) > 0.01)
- return FALSE;
-
- for (i = 0; i < 3; i++)
- if (fabs(p1->normal[i] - p2->normal[i]) > 0.001)
- return FALSE;
-
- return TRUE;
- }
-
- struct seperatingplane *Findpassages(register struct winding *source, register struct winding *pass)
- {
- int i, j, k, l;
- struct plane plane;
- vec3_t v1, v2;
- float d;
- double length;
- int counts[3];
- bool fliptest;
- struct seperatingplane *sep, *list;
-
- list = NULL;
-
- /* check all combinations */
- for (i = 0; i < source->numpoints; i++) {
- l = (i + 1) % source->numpoints;
- VectorSubtract(source->points[l], source->points[i], v1);
-
- /*
- * fing a vertex of pass that makes a plane that puts all of the
- * vertexes of pass on the front side and all of the vertexes of
- * source on the back side
- */
- for (j = 0; j < pass->numpoints; j++) {
- VectorSubtract(pass->points[j], source->points[i], v2);
-
- plane.normal[0] = v1[1] * v2[2] - v1[2] * v2[1];
- plane.normal[1] = v1[2] * v2[0] - v1[0] * v2[2];
- plane.normal[2] = v1[0] * v2[1] - v1[1] * v2[0];
-
- /* if points don't make a valid plane, skip it */
-
- length = plane.normal[0] * plane.normal[0]
- + plane.normal[1] * plane.normal[1]
- + plane.normal[2] * plane.normal[2];
-
- if (length < ON_EPSILON)
- continue;
-
- length = 1 / sqrt(length);
-
- plane.normal[0] *= length;
- plane.normal[1] *= length;
- plane.normal[2] *= length;
-
- plane.dist = DotProduct(pass->points[j], plane.normal);
-
- /*
- * find out which side of the generated seperating plane has the
- * source portal
- */
- fliptest = FALSE;
- for (k = 0; k < source->numpoints; k++) {
- if (k == i || k == l)
- continue;
- d = DotProduct(source->points[k], plane.normal) - plane.dist;
- if (d < -ON_EPSILON) { /* source is on the negative side, so we want all */
- /* pass and target on the positive side */
-
- fliptest = FALSE;
- break;
- }
- else if (d > ON_EPSILON) { /* source is on the positive side, so we want all */
- /* pass and target on the negative side */
-
- fliptest = TRUE;
- break;
- }
- }
- if (k == source->numpoints)
- continue; /* planar with source portal */
-
- /* flip the normal if the source portal is backwards */
- if (fliptest) {
- VectorNegate(plane.normal);
- plane.dist = -plane.dist;
- }
-
- /*
- * if all of the pass portal points are now on the positive side,
- * this is the seperating plane
- */
- counts[0] = counts[1] = counts[2] = 0;
- for (k = 0; k < pass->numpoints; k++) {
- if (k == j)
- continue;
- d = DotProduct(pass->points[k], plane.normal) - plane.dist;
- if (d < -ON_EPSILON)
- break;
- else if (d > ON_EPSILON)
- counts[0]++;
- else
- counts[2]++;
- }
- if (k != pass->numpoints)
- continue; /* points on negative side, not a seperating plane */
-
- if (!counts[0])
- continue; /* planar with pass portal */
-
- /* save this out */
- count_sep++;
-
- if (!(sep = (struct seperatingplane *)kmalloc(sizeof(struct seperatingplane))))
- Error(failed_memoryunsize, "seperate plane");
-
- sep->next = list;
- list = sep;
- sep->plane = plane;
- }
- }
-
- return list;
- }
-
- /*
- * ============
- * CalcPassages
- * ============
- */
- void CalcPassages(void)
- {
- int i, j, k;
- int count, count2;
- struct visleaf *l;
- struct visportal *p1, *p2;
- struct seperatingplane *sep;
- struct passage *passages;
-
- mprintf(" - building passages...\n");
-
- count = count2 = 0;
- for (i = 0; i < num_visleafs; i++) {
- l = leafs[i];
-
- for (j = 0; j < l->numportals; j++) {
- p1 = l->portals[j];
- for (k = 0; k < l->numportals; k++) {
- if (k == j)
- continue;
-
- count++;
- p2 = l->portals[k];
-
- /* definately can't see into a coplanar portal */
- if (PlaneCompare(&p1->plane, &p2->plane))
- continue;
-
- count2++;
-
- sep = Findpassages(p1->winding, p2->winding);
- if (!sep) {
- count_sep++;
- if (!(sep = (struct seperatingplane *)kmalloc(sizeof(struct seperatingplane))))
- Error(failed_memoryunsize, "seperate plane");
-
- sep->next = NULL;
- sep->plane = p1->plane;
- }
- if (!(passages = (struct passage *)kmalloc(sizeof(struct passage))))
- Error(failed_memoryunsize, "passage");
-
- passages->planes = sep;
- passages->from = p1->leaf;
- passages->to = p2->leaf;
- passages->next = l->passages;
- l->passages = passages;
- }
- }
- }
-
- mprintf("%5i numpassages (%i)\n", count2, count);
- mprintf("%5i total passages\n", count_sep);
- }
-
- /*============================================================================= */
-
- bool vis(__memBase, int level, char *prtBuf)
- {
- int i;
-
- mprintf("----- Vis ---------------\n");
-
- AllocClusters(bspMem, LUMP_VISIBILITY);
-
- if (!(bspMem->visOptions & VIS_MEM))
- LoadPortals(prtBuf);
- if (level)
- testvislevel = level;
-
- originalvismapsize = num_visportals * ((num_visportals + 7) / 8);
- bitbytes = ((num_visportals + 63) & ~63) >> 3;
- bitlongs = bitbytes / sizeof(long long int);
-
- if (!(uncompressed = (unsigned char *)kmalloc(bitbytes * num_visleafs)))
- Error(failed_memoryunsize, "bitbytes");
-
- mprintf("----- CalcVis -----------\n");
- /*CalcPassages (); */
- CalcVis(bspMem);
-
- mprintf("%5i c_chains\n", c_chains);
- mprintf("%5i visdatasize (compressed from %i)\n", bspMem->shared.quake1.visdatasize, originalvismapsize);
-
- mprintf("----- CalcAmbientSounds -\n");
- CalcAmbientSounds(bspMem);
-
- for (i = 0; i < num_visleafs; i++)
- if (leafs[i])
- FreeLeaf(leafs[i]);
- for (i = 0; i < (num_visportals * 2); i++)
- if (portals[i].winding);
- FreeWinding(portals[i].winding);
- tfree(leafs);
- tfree(portals);
- kfree();
-
- return TRUE;
- }
-